/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.icee.myth.console;

import com.icee.myth.common.ServerAddress;
import com.icee.myth.common.message.serverMessage.Message;
import com.icee.myth.common.messageQueue.ServerMessageQueue;
import com.icee.myth.console.channelHandler.ConsoleToManagerHandler;
import com.icee.myth.console.encoder.ConsoleToManagerEncoder;
import com.icee.myth.console.message.serverMessage.*;
import com.icee.myth.protobuf.ConsoleToManagerProtocol.ServerStatus;
import com.icee.myth.protobuf.ConsoleToManagerProtocol.ServerStatuses;
import com.icee.myth.protobuf.ExternalCommonProtocol.VariableValueProto;
import com.icee.myth.protobuf.builder.ConsoleToManagerBuilder;
import com.icee.myth.utils.*;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * Console是管理员对各游戏服务器操控的控制台
 * Console将管理员指令发给Server处理
 *
 * @author liuxianke
 */
public class Console implements Runnable {

    //Singleton，没有多线程同步的需要，只是方便全局访问
    private static Console INSTANCE = new Console();
    // 全局共享的消息队列
    private final LinkedTransferQueue<Message> messageQueue = ServerMessageQueue.queue();
    private ConsoleToManagerClient consoleToManagerClient; // Manager Client
    private ServerAddress managerAddress;   // Manager服务地址

    //getInstance操作的第一次调用在main中，因此无需同步
    public static Console getInstance() {
        return INSTANCE;
    }

    private Console() {
    }

    /*main函数参数说明
    * 完整：
java -ea -Dfile.encoding=UTF-8 -classpath dist\ProjectKManagerConsole.jar com.icee.myth.console.Console -managerHost 192.168.100.202 -managerPort 4678

说明：
java -ea -Dfile.encoding=UTF-8 -classpath dist\ProjectKManagerConsole.jar com.icee.myth.console.Console (这个不需要变化)
-managerHost  --- Manager IP地址  	(需要修改)
-managerPort  --- Manager 端口		(需要修改)
    * */
    public static void main(String[] args) {
        if (args[0].compareTo("-version") == 0) {
            System.out.println("Console version : " + Version.getBuildVersion());
            return;
        }

        // initialize logger
        MLogger.init("console", LogConsts.LOGFLUSH_INTERVAL);

        // 从配置文件读取配置信息
        if (!Console.getInstance().parseArgs(args)) {
            return;
        }

        Console.getInstance().startOnlyOnce();
    }

    private String parseArg(String[] args, String param) {
        int argsNum = args.length;
        String retVal = null;
        for (int i = 0; i < argsNum; i++) {
            if (args[i].compareTo(param) == 0) {
                if (i + 1 < argsNum) {
                    retVal = args[i + 1];
                }
                break;
            }
        }

        return retVal;
    }

    private boolean parseArgs(String[] args) {
        // parse manager host and port
        String managerHost = parseArg(args, "-managerHost");
        if (managerHost != null) {
            String managerPortStr = parseArg(args, "-managerPort");
            if (managerPortStr != null) {
                Integer managerPort = Integer.valueOf(managerPortStr);
                if (managerPort != null) {
                    managerAddress = new ServerAddress(managerHost, managerPort);
                } else {
                    System.err.println("Value of \"-managerPort\" param must be integer.");
                    return false;
                }
            } else {
                System.err.println("Lack \"-managerPort XXX\" param");
                return false;
            }
        } else {
            System.err.println("Lack \"-managerHost XXX\" param");
            return false;
        }
        return true;
    }

    //    private void config(String host, int port) {
//        managerAddress = new ServerAddress(host, port);
//    }
    public void startOnlyOnce() {
        // 连接manager server
        LengthFieldBasedFrameDecoder lengthFieldBasedFrameDecoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 2, 4, 0, 0);
        ConsoleToManagerEncoder consoleToManagerEncoder = new ConsoleToManagerEncoder();
        ConsoleToManagerHandler toManagerHandler = new ConsoleToManagerHandler();
        consoleToManagerClient = ConsoleToManagerClient.getInstance();
        consoleToManagerClient.init(managerAddress.getHost(), managerAddress.getPort(), lengthFieldBasedFrameDecoder, consoleToManagerEncoder, toManagerHandler);
        consoleToManagerClient.connectToServer();

        // 启动命令输入线程
        new Thread(new ConsoleCommander()).start();
        // 启动消息处理线程
        new Thread(this).start();
    }

    private void handleMessages() {
        // 不处理shuttingDown过程中产生的消息
        Message msg = messageQueue.poll();

        while (msg != null) {
            switch (msg.getType()) {
                case CONSOLE_MANAGER_CONNECT:
                case CONSOLE_MANAGER_CLOSE: {
                    // 处理Cluster Connect和Close消息
                    consoleToManagerClient.handleMessage(msg);
                    break;
                }
                case CONSOLE_COMMAND: {
                    // TODO: 向Manager发命令
                    Channel managerChannel = consoleToManagerClient.getManagerChannel();
                    if (managerChannel != null) {
                        CommandConsoleMessage commandConsoleMessage = (CommandConsoleMessage) msg;
                        switch (commandConsoleMessage.cmdOpcode) {
                            case Consts.CONSOLE_COMMAND_RUN_SERVER: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildRunServer(serverId));
                                    } else {
                                        System.out.println("Please input [run serverID] and serverID is an integer");
                                    }
                                } else {
                                    System.out.println("Please input [run serverID] and serverID is an integer");
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_SHUTDOWN_SERVER: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildShutdownServer(serverId));
                                    } else {
                                        System.out.println("Please input [shutdown serverID] and serverID is an integer");
                                    }
                                } else {
                                    System.out.println("Please input [shutdown serverID] and serverID is an integer");
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_SYNCCONFIG: {
                                managerChannel.write(ConsoleToManagerBuilder.buildSyncConfig());
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_GET_SERVER_STATE: {
                                managerChannel.write(ConsoleToManagerBuilder.buildGetServerState());
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_DEBUG_RUNSERVER: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildDebugRunServer(serverId));
                                    } else {
                                        System.out.println("Please input [drun serverID] and serverID is an integer");
                                    }
                                } else {
                                    System.out.println("Please input [drun serverID] and serverID is an integer");
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_GM: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() >= 3)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildGm(commandConsoleMessage.strArgs));
                                    } else {
                                        System.out.println("Please input serverID playerId gmCmd gmargs");
                                    }
                                } else {
                                    System.out.println("Please input serverID playerId gmCmd gmargs");
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_CLEAR_TEST_FLAG: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildClearTestFlag(serverId));
                                    } else {
                                        System.out.println("Please input [cleartestflag serverID] and serverID is an integer");
                                    }
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_SET_TEST_FLAG: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildSetTestFlag(serverId));
                                    } else {
                                        System.out.println("Please input [settestflag serverID] and serverID is an integer");
                                    }
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_FORCE_SET_SERVER_TO_SHUTDOWN_STATUS: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildForceSetServerShutdownStatus(serverId));
                                    } else {
                                        System.out.println("Please input [clear serverID] and serverID is an integer");
                                    }
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_LOAD_SERVER_CONFIG: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    Integer serverId = Integer.valueOf(commandConsoleMessage.strArgs.get(0));
                                    if (serverId != null) {
                                        managerChannel.write(ConsoleToManagerBuilder.buildLoadServerConfig(serverId));
                                    } else {
                                        System.out.println("Please input [loadsvrconfig serverID] and serverID is an integer");
                                    }
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_SET_DATE: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    String time = commandConsoleMessage.strArgs.get(0);
                                    if (time != null) {
                                        try {
                                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                                            Date dt = sdf.parse(time);
                                            managerChannel.write(ConsoleToManagerBuilder.buildSetDate(dt.getTime()));
                                        } catch (ParseException ex) {
                                            System.out.println("Please input [setdate yyyy-MM-dd]");
                                        }
                                    } else {
                                        System.out.println("Please input [setdate yyyy-MM-dd]");
                                    }
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_SET_TIME: {
                                if ((commandConsoleMessage.strArgs != null) && (commandConsoleMessage.strArgs.size() == 1)) {
                                    String time = commandConsoleMessage.strArgs.get(0);
                                    if (time != null) {
                                        try {
                                            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
                                            Date dt = sdf.parse(time);
                                            managerChannel.write(ConsoleToManagerBuilder.buildSetTime(dt.getTime()));
                                        } catch (ParseException ex) {
                                            System.out.println("Please input [settime HH:mm:ss]");
                                        }
                                    } else {
                                        System.out.println("Please input [settime HH:mm:ss]");
                                    }
                                }
                                break;
                            }
                            case Consts.CONSOLE_COMMAND_GET_DATETIME: {
                                managerChannel.write(ConsoleToManagerBuilder.buildGetDateTime());
                                break;
                            }
                        }
                    }
                    break;
                }
                case CONSOLE_SERVER_START_OK: {
                    ServerIdConsoleMessage serverIdConsoleMessage = (ServerIdConsoleMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + serverIdConsoleMessage.serverId + " running.");
                    break;
                }
                case CONSOLE_SERVER_SHUTDOWN_OK: {
                    ServerIdConsoleMessage serverIdConsoleMessage = (ServerIdConsoleMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + serverIdConsoleMessage.serverId + " is shutdown.");
                    break;
                }
                case CONSOLE_SYNCCONFIG_RETURN: {
                    SyncConfigReturnConsoleMessage syncConfigReturnConsoleMessage = (SyncConfigReturnConsoleMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "SyncConfig " + ((syncConfigReturnConsoleMessage.result == 0) ? "OK." : "fail."));
                    break;
                }
                case CONSOLE_GET_SERVER_STATUS_RETURN: {
                    GetServerStatusReturnConsoleMessage getServerStatusReturnConsoleMessage = (GetServerStatusReturnConsoleMessage) msg;
                    ServerStatuses statuses = getServerStatusReturnConsoleMessage.statuses;
                    int serverNum = statuses.getStatusesCount();
                    for (int i = 0; i < serverNum; i++) {
                        ServerStatus status = statuses.getStatuses(i);
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server[ " + status.getId() + "] status : " + getStatusFromStatusCode(status.getStatus()));
                    }
                    break;
                }
                case CONSOLE_FORCE_SET_SERVER_SHUTDOWN_STATUS_OK: {
                    ServerIdConsoleMessage serverIdConsoleMessage = (ServerIdConsoleMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + serverIdConsoleMessage.serverId + " clear OK.");
                    break;
                }
                case CONSOLE_FORCE_SET_SERVER_SHUTDOWN_STATUS_ERROR: {
                    ServerIdConsoleMessage serverIdConsoleMessage = (ServerIdConsoleMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + serverIdConsoleMessage.serverId + " clear fail.");
                    break;
                }
                case CONSOLE_LOAD_SERVER_CONFIG_OK: {
                    ServerIdConsoleMessage serverIdConsoleMessage = (ServerIdConsoleMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + serverIdConsoleMessage.serverId + " load config OK.");
                    break;
                }
                case CONSOLE_LOAD_SERVER_CONFIG_ERROR: {
                    LoadServerConfigErrorConsoleMessage loadServerConfigErrorConsoleMessage = (LoadServerConfigErrorConsoleMessage) msg;
                    switch (loadServerConfigErrorConsoleMessage.errorCode) {
                        case Consts.LOADSERVERCONFIG_ERROR_NOT_SHUTDOWN: {
                            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + loadServerConfigErrorConsoleMessage.serverId + " load config error because server not shutdown.");
                            break;
                        }
                        case Consts.LOADSERVERCONFIG_ERROR_NOT_FOUND: {
                            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + loadServerConfigErrorConsoleMessage.serverId + " load config error because config not found.");
                            break;
                        }
                        default: {
                            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Server " + loadServerConfigErrorConsoleMessage.serverId + " load config error for unknown reason.");
                            break;
                        }
                    }
                    break;
                }
                case CONSOLE_BATTLE_WIN_RATE_RESULT: {
                    BattleWinRateResultMessage battleWinRateResultMessage = (BattleWinRateResultMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Battle win rate result: win " + battleWinRateResultMessage.winNum + " lost " + battleWinRateResultMessage.lostNum + " draw " + battleWinRateResultMessage.drawNum + ".");
                    break;
                }
                case CONSOLE_CARD_DRAW_RATE_RESULT: {
                    CardDrawRateResultMessage cardDrawRateResultMessage = (CardDrawRateResultMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Battle win rate result: ");

                    List<VariableValueProto> results = cardDrawRateResultMessage.result.getValuesList();
                    for (VariableValueProto result : results) {
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Card id:" + result.getId() + " count:" + result.getValue());
                    }
                    break;
                }
                case CONSOLE_UNINIT_OCCUPY_INFO: {
                    UninitOccupyInfoMessage uninitOccupyInfoMessage = (UninitOccupyInfoMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "====== uninit occupy info begin ======");

                    List<Integer> results = uninitOccupyInfoMessage.intValuesProto.getValuesList();
                    for (Integer result : results) {
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Uninit occupy id: " + result);
                    }
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "====== uninit occupy info end ======");
                    break;
                }
                case CONSOLE_FIGHTING_OCCUPY_INFO: {
                    FightingOccupyInfoMessage fightingOccupyInfoMessage = (FightingOccupyInfoMessage) msg;
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "====== fighting occupy info begin ======");

                    List<Integer> results = fightingOccupyInfoMessage.intValuesProto.getValuesList();
                    for (Integer result : results) {
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Fighting occupy id: " + result);
                    }
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "====== fighting occupy info end ======");
                    break;
                }
                case CONSOLE_DATE_TIME: {
                    DateTimeMessage dateTimeMessage = (DateTimeMessage) msg;

                    Date dt = new Date(dateTimeMessage.datetime);
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String sDateTime = sdf.format(dt);
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Manager Server time: " + sDateTime);
                    break;
                }
            }
            msg = messageQueue.poll();
        }
    }

    @Override
    public void run() {
        while (true) {
            // handle message
            handleMessages();

            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        }
    }

    private String getStatusFromStatusCode(int statusCode) {
        switch (statusCode) {
            case 0: {
                return "Unkown";
            }
            case 1: {
                return "Booting";
            }
            case 2: {
                return "Running";
            }
            case 3: {
                return "Closing";
            }
            case 4: {
                return "Shutdown";
            }
            case 5: {
                return "AbnormalRunning";
            }
            case 6: {
                return "Dummy";
            }
            case 7: {
                return "Testing";
            }
        }
        return "Unkown";
    }

}
